package cn.summit.core.config;

import cn.summit.core.authentication.authorize.AuthorizeConfigurerManager;
import cn.summit.core.authentication.handler.CustomLogoutHandler;
import cn.summit.core.authentication.kaptcha.KaotchaImageFilter;
import cn.summit.core.mobile.MobileAuthenticationConfig;
import cn.summit.core.mobile.MobileValidateFilter;
import cn.summit.core.properties.SecurityProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;

/**
 * spring security配置
 *
 * @author summit
 * @since 2020/3/3 21:00
 */
@Configuration
@EnableWebSecurity
@EnableConfigurationProperties(SecurityProperties.class)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    private UserDetailsService customUserDetailsService;

    @Autowired
    private SecurityProperties securityProperties;

    @Bean
    public PasswordEncoder passwordEncoder() {
        // 明文+随机盐值》加密存储
        return new BCryptPasswordEncoder();
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // auth.inMemoryAuthentication().withUser("summit").password(passwordEncoder().encode("123"))
        //     .authorities("test");
        auth.userDetailsService(customUserDetailsService);
    }


    @Autowired
    private AuthenticationSuccessHandler customSuccessHandler;

    @Autowired
    private AuthenticationFailureHandler customfailureHandler;

    @Autowired
    private KaotchaImageFilter kaotchaImageFilter;

    // 校验手机验证码
    @Autowired
    private MobileValidateFilter mobileValidateFilter;

    // 校验手机号是否存在，就是手机号认证
    @Autowired
    private MobileAuthenticationConfig mobileAuthenticationConfig;


    @Autowired
    private SessionInformationExpiredStrategy sessionInformationExpiredStrategy;

    /**
     * session失效处理
     */
    @Autowired
    private InvalidSessionStrategy invalidSessionStrategy;

    @Autowired
    private CustomLogoutHandler customLogoutHandler;

    @Autowired
    private AuthorizeConfigurerManager authorizeConfigurerManager;


    /**
     * 为了解决退出重新登录问题
     *
     * @return
     */
    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // String[] noAuthUrl = {securityProperties.getAuthentication().getLoginPage(), "/code/image",
        //     MyConstant.mobile_from_url, MyConstant.mobile_page_url, "/mobile/form"};
        //        http.httpBasic() // 采用 httpBasic认证方式
        http.addFilterBefore(mobileValidateFilter, UsernamePasswordAuthenticationFilter.class)
            .addFilterBefore(kaotchaImageFilter, UsernamePasswordAuthenticationFilter.class)
            .formLogin() // 表单登录方式
            .loginPage(securityProperties.getAuthentication().getLoginPage()).loginProcessingUrl(
            securityProperties.getAuthentication()
                .getLoginProcessingUrl()) // 登录表单提交处理url, 默认是/login
            //默认的是 username
            .usernameParameter(securityProperties.getAuthentication().getUsernameParameter())
            // 默认的是 password
            .passwordParameter(securityProperties.getAuthentication().getPasswordParameter())
            .successHandler(customSuccessHandler).failureHandler(customfailureHandler).and()
            //  .authorizeRequests() // 认证请求
            // 放行 不需要认证可访问
            // .antMatchers(noAuthUrl).permitAll().anyRequest()
            // .authenticated() //所有访问该应用的http请求都要通过身份认证才可以访问
            // .and()
            .sessionManagement()
            //session失效处理
            .invalidSessionStrategy(invalidSessionStrategy)
            //最多同时一个session在线
            .maximumSessions(1)
            // 当用户达到最大session数后，则调用此处的实现
            .expiredSessionStrategy(sessionInformationExpiredStrategy)
            //当一个用户达到最大session数,则不允许后面再登录
            .maxSessionsPreventsLogin(true).sessionRegistry(sessionRegistry())
            .and().and().logout()
            .addLogoutHandler(customLogoutHandler)
            .logoutUrl("/user/logout") // 退出请求路径
            .logoutSuccessUrl("/mobile/page") //退出成功后跳转地址
            .deleteCookies("JSESSIONID") // 退出后删除什么cookie值
       ;
        //将手机认证添加到过滤器链上
        http.apply(mobileAuthenticationConfig);
        // 关闭跨站请求伪造
        http.csrf().disable();

        // 将所有的授权配置统一的起来
        authorizeConfigurerManager.configure(http.authorizeRequests());
    }


    /**
     * 一般是针对静态资源放行
     *
     * @param web webSecurity
     * @throws Exception 异常
     */
    @Override
    public void configure(WebSecurity web) {
        web.ignoring().antMatchers("/dist/**", "/modules/**", "/plugins/**");
    }
}
